package ws

import (
	"sync"

	"github.com/gorilla/websocket"
	"xgame.go_server/biz_server/cmd_handler"
	"xgame.go_server/comm/log"
	"xgame.go_server/comm/main_thread"
	"xgame.go_server/msg"
)

// GatewayServerConn 网关服务器连接
type GatewayServerConn struct {
	gatewayServerId int32
	wsConn          *websocket.Conn
	sendMsgQ        chan *msg.InnerCmd // BlockingQueue
	cmdCtxMap       *sync.Map
}

// 循环发送消息
func (this *GatewayServerConn) startLoopSendMsg() {
	// 首先构建发送队列
	this.sendMsgQ = make(chan *msg.InnerCmd, 64)

	go func() { // new Thread().start(() -> { ... })
		for {
			msgObj := <-this.sendMsgQ // queue.pop

			if nil == msgObj {
				continue
			}

			byteArray, err := msgObj.ToByteArray()

			if nil != err {
				log.Error("%+v", err)
				return
			}

			if err := this.wsConn.WriteMessage(websocket.BinaryMessage, byteArray); nil != err {
				log.Error("%+v", err)
			}
		}
	}() // 相当于启动一个线程, 专门负责发送消息
}

// 循环读取消息
func (this *GatewayServerConn) loopReadMsg() {
	this.cmdCtxMap = &sync.Map{}

	for {
		_, msgData, err := this.wsConn.ReadMessage()

		if nil != err {
			log.Error("%+v", err)
			break
		}

		func() {
			defer func() {
				if err := recover(); nil != err {
					log.Error("发生异常, %+v", err)
				}
			}()

			// 通过内部服务器消息解包
			innerCmd := &msg.InnerCmd{}

			if err := innerCmd.FromByteArray(msgData); nil != err {
				log.Error("%+v", err)
				return
			}

			realData := innerCmd.MsgData

			msgCode, newMsgX, err := msg.Decode(realData)

			if nil != err {
				log.Error(
					"消息解码错误, msgCode = %d, error = %+v",
					msgCode, err,
				)
				return
			}

			log.Debug(
				"收到客户端消息, gatewayServerId = %d, sessionUId = %s, userId = %d, msgCode = %d, msgName = %s",
				innerCmd.GatewayServerId,
				innerCmd.SessionUId,
				innerCmd.UserId,
				msgCode,
				newMsgX.Descriptor().Name(),
			)

			// 创建指令处理器
			handlerFunc := cmd_handler.GetHandlerFunc(msgCode)

			if nil == handlerFunc {
				log.Error(
					"未找到指令处理器, msgCode = %d",
					msgCode,
				)
				return
			}

			ctx, loadOk := this.cmdCtxMap.Load(innerCmd.SessionUId)

			if !loadOk {
				ctx = &cmdCtx{
					gatewayServerId:   innerCmd.GatewayServerId,
					sessionUId:        innerCmd.SessionUId,
					userId:            innerCmd.UserId,
					GatewayServerConn: this,
				}

				this.cmdCtxMap.Store(innerCmd.SessionUId, ctx)
			}

			main_thread.Process(func() {
				handlerFunc(ctx.(cmd_handler.MyCmdCtx), newMsgX) // ???
			})
		}()
	}
}
